Skip to content

Add Personal Timesheet View to Me Tab on Dashboard - #469

Merged
Dharp02 merged 66 commits into
mainfrom
redesign-ui
Aug 7, 2026
Merged

Add Personal Timesheet View to Me Tab on Dashboard#469
Dharp02 merged 66 commits into
mainfrom
redesign-ui

Conversation

@Dharp02

@Dharp02 Dharp02 commented Aug 3, 2026

Copy link
Copy Markdown
Collaborator

Overview

This branch implements the full mobile/desktop UI redesign for TimeHuddle — bringing the app to parity with the redesign prototype across layout, navigation, and interaction patterns — plus several quality-of-life fixes and feature additions on top.

Changes by Commit

Navigation & Shell

  • Swap Settings for Huddle in mobile bottom nav — Huddle replaces Settings in the 4-item bottom nav; Settings moves to the sidebar
  • Reorder desktop sidebar Workspace nav — Clock moved up, Work moved to the bottom to match prototype priority
  • Sidebar: embed org/team switcher inline on desktop — then reverted after testing revealed conflicts with the existing popover approach

OrgTeamSwitcher

  • Rebuild OrgTeamSwitcher as prototype-matching bottom-sheet/dialog Modal — full rebuild matching the prototype's trigger button and animated sheet
  • OrgTeamSwitcher: match prototype exactly — refined trigger appearance and transition animation

Dashboard

  • Dashboard: 4-column stat row at desktop widths — stat cards expand to a single 4-col row on wider screens
  • Dashboard: add Me/Team toggle beside the title, scoped stats per tab — Me tab shows your own numbers; Team tab shows the whole team

Clock Page

  • Clock: cap layout to centered max-w-2xl on desktop — content column constrained and centered
  • Clock page: full attach bar, bold session timer, recent sessions — full-width attach bar, bolder session timer, recent sessions shown inline
  • Clock page: widen column, stop dimming the bottom-nav FAB — wider content column; FAB no longer grayed out while clocked in

Huddle

  • Huddle: edge-to-edge feed on mobile — feed goes full width on small screens
  • Huddle: flatten composer chrome, widen feed column — composer chrome reduced; feed column widened on desktop

Tickets

  • Tickets filters: horizontally scrollable chip row on mobile — filter chips scroll horizontally instead of wrapping
  • Tickets: narrow to centered max-w-4xl column on desktop — content column constrained
  • Tickets: fix clipped ticket options menu — overflow clipping on the options dropdown fixed

Teams Page

  • Redesign Teams page: move admin timesheet to Dashboard, add team pill switcher — admin timesheet panel relocated to Dashboard → Team → Timesheet; Teams page gets a pill-style team switcher

Modals & Toasts

  • Mobile bottom-sheet variant for @mieweb/ui Modal — Modal slides up as a sheet on mobile (<768px), centered dialog on desktop
  • Confirm desktop centered-dialog Modal variant preserved — verified modal behavior across breakpoints
  • Toast: shrink-to-content and center at desktop widths — toasts no longer stretch full-width on desktop
  • Fix modals to appear centered instead of full-screen on mobile — corrected modal positioning regression

New Features

  • Add personal timesheet view to Me tab on Dashboard — Overview/Timesheet secondary toggle on the Me tab; loads the current user's own clock history via AdminTimesheetPanel
  • Add share button to team header with compact icon-button strip — share/copy team join link from the team header
  • Add header notification bell and sidebar app info/test push — notification bell in the app header; app version/info in sidebar footer

Docs

  • Add redesign prototype and mobile/desktop implementation plan — prototype HTML and phased implementation checklist added to design/
  • docs(redesign): mark acceptance criteria — tracked completed phases in the plan doc

Acceptance Criteria

  • Mobile bottom nav shows Clock, Huddle, Tickets, Dashboard
  • OrgTeamSwitcher opens as a bottom sheet on mobile, centered modal on desktop
  • Dashboard Me/Team toggle scopes all stat cards correctly
  • Me tab Timesheet view loads current user's clock history
  • Admin timesheet accessible via Dashboard → Team → Timesheet
  • Modals are centered dialogs on desktop, bottom sheets on mobile
  • Toasts are centered and content-width on desktop
  • npm run typecheck and npm run lint pass clean
Screenshot 2026-08-04 at 3 25 18 PM Screenshot 2026-08-04 at 3 25 38 PM Screenshot 2026-08-04 at 3 25 58 PM Screenshot 2026-08-04 at 3 26 19 PM Screenshot 2026-08-04 at 3 26 42 PM Screenshot 2026-08-04 at 3 27 14 PM Screenshot 2026-08-04 at 3 27 29 PM

Dharp02 added 30 commits August 3, 2026 12:09
Step 1 of the redesign plan. Bottom nav tabs are now Dashboard, Huddle,
Clock (center FAB), Tickets, Teams. Settings is still reachable via the
avatar dropdown in the header, matching the prototype's priority order.
Step 2 of the redesign plan. Adds a CSS-only override that turns the
@mieweb/ui Modal into a bottom-sheet on mobile: slides up from the
bottom, full width, rounded top corners only, capped at 88dvh with
internal scroll. Applies to every consumer of the shared Modal (tickets,
teams, org, invite, confirm-delete, feedback, media, etc.) without
forking the component. Desktop behavior is unchanged.
Step 3. Huddle now uses AppPage flush + fill so PostCards stretch to the
viewport edges on mobile (no side gutter, no card radius). Tabs,
composer, drafts, and search input keep px-4 mobile gutter so they still
look inset. Desktop keeps the max-w-4xl reading column (Phase 2 will
tighten this to max-w-2xl). No changes to composer logic or post
rendering.
The quick-stats grid in DashboardPage.tsx already uses
'grid grid-cols-2 gap-3', which satisfies the mobile 2-column requirement
from the redesign plan. Me/Team scope is controlled by the existing
OrgTeamSwitcher / TeamContext, not a separate toggle inside the page, so
no layout change is needed for step 4. Numbers populate correctly for
both personal and team scopes.
Step 5. ClockPage was already a single-column stack (banner status ->
plan/wrap-up composer -> primary action -> punch-clock display). On
mobile the primary action row (Post plan and clock in / Post wrap-up
and clock out / Clock in / Clock out / Break) now stacks vertically
with each Button spanning w-full so the tap target reaches the full
column width. Desktop keeps the horizontal flex-wrap row via
sm:flex-row + sm:w-auto.

Plan-first gate logic in useClockToggle is untouched -- all 11 tests in
useClockToggle.test.ts still pass.
Replaces the anchored Dropdown popover with the shared Modal primitive:
- Mobile: full-width bottom sheet (existing Modal CSS from step 2)
- Desktop: centered dialog (existing Modal CSS from step 10)
- Organization select + Team list with member counts + '+ New team',
  matching design/redesign-prototype/index.html's OrgTeamSwitcher exactly
- Portals the Modal to document.body so it escapes AppHeader's
  backdrop-blur containing block (backdrop-filter creates a new
  containing block for position:fixed descendants, which was pinning
  the sheet near the header instead of the viewport)
- Preserves existing features not in the prototype: org role labels,
  pending join-request badges, awaiting-approval section
- Header trigger now matches the prototype's stacked two-line layout:
  brand mark + bold team name on top, muted 'Org name ▾' below —
  replacing the single-line 'Org > Team' pill.
- Replaced the large translateY(100%) slide-up keyframe with the
  prototype Modal's actual animation: a quiet fade + 4px settle
  (150ms ease-out). The prototype never really 'slides' a large
  distance — it docks the sheet to the bottom and just fades/settles
  it in, which is what read as clean instead of a 'jump'.
- AppPage gains an optional titleActions slot so pages can render
  controls on the same row as the page title (right-aligned), used
  here for the Me/Team segmented toggle from the redesign prototype.
- DashboardPage: 'Me' (default) shows the signed-in user's own hours
  today, open/closed/high-priority tickets, and running timers; 'Team'
  shows the existing team-wide aggregates unchanged.
- The toggle is always visible now (previously personal-team users had
  the team members / time-logged sections silently hidden via
  isPersonalTeam) — switching to Team on a personal team just shows a
  team of one instead of hiding the section.
The prototype's desktop sidebar renders the Organization select + Team
list + '+ New team' inline, always visible — no header button/modal.
Extracted that content into OrgTeamSwitcherPanel (shared with the
mobile bottom sheet) and render it directly in Sidebar.tsx's brand
block for the expanded desktop rail. The header pill + Modal in
OrgTeamSwitcher is now md:hidden (mobile-only) since desktop no longer
needs it — mobile behavior is unchanged.
… switcher

- Teams page: replace tabs with always-visible Members/Pending sections
- Add horizontal-scroll team pill switcher (Personal + selected team lead, rest follow)
- Consolidate Rename/Delete/Copy/Share into Team Settings modal; keep code+copy under team name
- Restore per-member 3-dot menu (Make/Remove Admin, Set Password, Remove Member) for all rows, including self (minus self-demote/remove)
- Compact icon-only Create/Join Team buttons in page title actions
- Move admin Timesheet view from Teams to Dashboard's Team tab with Overview/Timesheet toggle and deep-link support
- Scope the mobile bottom-sheet Modal treatment to only the org/team switcher modal, restoring default centered dialogs everywhere else
- Update e2e tests and notification deep links to match
Give the plan/wrap-up composer the same Photo/Video/Doc/Pulse/Ticket/
@Mention bar as the Huddle composer (extracted into shared
ComposerAttachButtons/ComposerChips so both stay in sync), and match the
redesign prototype's status card (bold elapsed-session timer instead of
the wall clock) and Recent sessions list.
- Override @mieweb/ui's mobile full-screen defaults (min-h-dvh, rounded-none)
  so all modals render as centered popups on every screen size
- Add horizontal margin on small screens so modals don't touch viewport edges
- Move org-switcher bottom-sheet rules outside the 767px media query so the
  bottom-sheet treatment applies on all screen sizes (not just mobile)
Drop the avatar circle and nested flex wrapper from the expanded
composer, trim its padding, and widen the feed's max-width from 2xl to
4xl so it isn't stranded in a narrow strip on wide dashboards.
Replace text "Copy" link with a tight code badge + copy + share icon
button group so the invite code row stays uncluttered. Share uses the
Web Share API with a clipboard fallback for desktop.
Match Clock's column width to Huddle's (max-w-2xl -> max-w-4xl). Also
drop the opacity/saturate dimming on the bottom-nav Clock In/Out FAB
when a plan or wrap-up is pending — it's a plain link to the clock
page, not a disabled control, so it shouldn't look blocked.
The per-row "..." menu used the plain library Dropdown, which
positions its menu absolutely inside the ticket list's
overflow-y-scroll container. Per the CSS overflow spec, once one axis
is scrollable the other axis clips too, so the menu was silently cut
off for rows near the bottom of the list -- the same class of bug
FilterDropdown already works around elsewhere in this file. Portal the
menu to document.body with fixed coordinates computed from the
trigger's rect, same pattern.
- Add meView state ('overview' | 'timesheet') to the Me tab
- Add Overview / Timesheet secondary toggle under Me tab
- Render AdminTimesheetPanel pre-loaded with the current user when meView is 'timesheet'
- Add userToTeamMember helper to convert TimecoreUser to TeamMember shape
Dharp02 added 3 commits August 4, 2026 20:23
- AppHeader: hide the clock-page shortcut on /app/clock so its 'Clock in' aria-label doesn't collide with the ClockPage's own Clock In button (Playwright strict-mode fail).

- ClockPage e2e page object: .clock-banner → .clock-status; status text 'On shift' → 'Clocked in'; plan gate text 'Write a plan before starting this session' → 'Plan before you clock in' — the underlying DOM was renamed during the redesign.

- Team switcher tests (team-qr-share, team-auto-accept, org-invite-by-email, timesheet-calc): getByRole('menuitem', {teamName}) → getByRole('dialog').getByRole('button', {teamName}); the switcher renders a modal of plain buttons now, not a menu.

- team-qr-share: 'Share team QR code' button lives inside Team Settings; the main teams page opens the share modal via 'Share team invite link'.

- teams: 'Copy' button is now 'Copy team code'; disambiguate the created team header by asserting the h3 CardTitle instead of unscoped getByText.

- dashboard: scope sidebar nav-item assertions to the 'Main navigation' nav, since the dashboard body now has its own Timesheet/Team tabs.

- timesheet-calc admin view: scope the 'Timesheet' tab click to <main> to skip the sidebar's identically-named nav button.
- playwright.config: bump retries 1→2 and per-test timeout 30s→45s; grant clipboard-read/write permission so copy-link flows can actually write to the clipboard (previously NotAllowedError).

- loginAs: after waitForURL('**/dashboard') also await waitForLoadState('networkidle') so the post-login session/orgs/teams fanout is settled before the test navigates — otherwise the next page.goto() occasionally races the session-cookie handshake and bounces back to the login screen.

- realtime specs (media-library, messages, notifications, team-members, timesheet): give the login toHaveURL check an explicit 15s timeout — the default 5s occasionally clips when the backend is warming up under sequential load.

- notifications:52, channels:140, pulsevault:348, notifications:28: test.setTimeout(60000) — these are the four historically flaky tests; 45s occasionally isn't enough late in the suite.

- profile-routing selectTestTeam: after page.reload(), wait for networkidle first, then require the Teams heading with a 20s timeout — the reload path is where the auth cookie sometimes hadn't propagated, dropping us on the login screen.

@jlocala1 jlocala1 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Went through the whole thing and clicked around the preview deploy. The redesign looks good and the commit list in the description made this way easier to follow than a 48 commit PR has any right to be. Couple of things I ran into.

The big one is the notification URLs, left that inline on notify-core.js. Short version is the old links already out there stop working and fail silently. One thing I couldn't check is how many old notifications are actually sitting around, so if those get cleaned up regularly this might be smaller than it looks.

Second thing: there are two personal timesheets now and they don't match. /app/timesheet is still routed and still linked from the sidebar, the More sheet and the command palette. I compared them on the preview with the same account. The old page has a Break Hours card and an Add Entry button. The new Me > Timesheet has neither, and it also shows a Member dropdown where the only option is yourself, since it's reusing AdminTimesheetPanel with a one element member list. So someone who finds the dashboard one first can't log a manual entry and doesn't see their break hours. Which one is meant to be the real one going forward? If it's the dashboard, the panel probably wants a way to hide the member select and the missing pieces ported over.

Also worth knowing, and not your fault: Playwright Checks and Backend Checks are both if: false in checks.yml, so the e2e changes in here and the retries and timeout bumps in playwright.config.ts never actually ran. The green checks are only lint, typecheck and build. Did you get a manual pass over the suite locally?

Two minor ones I didn't bother with inline. The styles.css modal overrides reach into @mieweb/ui internals (data-slot, data-state) with !important across every modal in the app, so a library bump could break it with nothing failing to catch it, might be worth a Modal variant upstream instead. And the More sheet in BottomNav has aria-modal="true" but focus stays on the page behind it when it opens, so keyboard users tab in from the top. If I'm already gone when these are sorted, feel free to dismiss this rather than waiting on me.

Comment thread meteor-backend/server/notify-core.js
Comment thread src/features/dashboard/DashboardPage.tsx Outdated
Comment thread src/features/dashboard/DashboardPage.tsx Outdated
Dharp02 added 15 commits August 5, 2026 19:30
Removes the decorative ClockStrand from the desktop header middle section. The header now shows only the timer pill and action buttons when clocked in.
…e ambient background CSS

Restores the original translateX-only keyframe. Removes all ambient-bg orb rules that were added for the background color experiment.
Removes the app-bg class and initAmbientBg hook call from AppLayout. Removes AmbientBgRow toggle from Settings Appearance section and deletes useAmbientBg.ts.
Removes the ClockStrand from the timer pill so mobile shows only the HH:MM:SS digits. Also removes the now-unused ClockStrand import.
Reduces gap, card padding, and timer font size on small screens. Hides the decorative wave strand on mobile. Shrinks the composer heading on mobile. All layout values restore to their original sizes at md breakpoint.
After updateSettings saves requirePlanForClock: true, fan-out a notification to every team member (except the admin who made the change) via createNotification, which persists to the inbox and fires a push.
Initialises teamFilter from selectedTeamId so the list opens pre-filtered to the active team. A useEffect also follows the team switcher so changing teams in the header updates the filter automatically.
…BottomNav focus trap, styles.css comment

- TeamsPage: forward old /app/teams?tab=timesheet notification URLs to /app/dashboard
- TeamContext: expose allTeams (unscoped) alongside scoped teams
- DashboardPage: switch org when deep-link teamId belongs to a different org
- BottomNav More sheet: move focus in on open, trap Tab/Shift+Tab, restore focus to trigger on close
- styles.css: document data-slot/data-state internals dependency with TODO to fix upstream
…k active tickets

- Remove Me/Team scoping from the Dashboard entirely
- Single Overview/Timesheet toggle in the content area (no more two-toggle confusion)
- Timesheet tab shows AdminTimesheetPanel for admins, PersonalTimesheetPanel for others
- Stats cards always show team data
- Active ticket rows are now clickable buttons that navigate to /app/tickets/:id
…ew/Timesheet

- Me/Team pill toggle back in the title bar
- Stats cards (Hours, Open tickets, Closed today, High priority) scope to Me or Team
- Team Members and Time logged today sections only appear on Team tab
- Single Overview/Timesheet toggle in content (no duplicate toggles)
- Active ticket rows remain clickable, navigating to /app/tickets/:id
@Dharp02
Dharp02 requested a review from jlocala1 August 6, 2026 01:55
@horner

horner commented Aug 6, 2026

Copy link
Copy Markdown
Member

🚀 Preview Deployment Ready

Service URL
App https://mieweb-timehuddle-redesign-ui.os.mieweb.org
API (Meteor) https://mieweb-timehuddle-redesign-ui-api.os.mieweb.org

Preview auto-deletes when this PR is closed.

Oh. I see it. Are there not test roles for auto logging in?

Every test environment should have a drop-down role that we can just login automatically with a single click

@jlocala1 jlocala1 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Re-checked all three and they're properly fixed, not papered over. The TeamsPage forward keeps memberId and teamId, the allTeams fallback handles the cross-org case, and consolidating onto one PersonalTimesheetPanel is a better outcome than what I asked for.

One new thing came out of the consolidation, left inline with a screenshot. Not a blocker in my view. Approving so this isn't stuck waiting on me, today is my last day.

↑ {membersClocked.length} active
{/* ── Timesheet view: admin panel for admins, personal panel for everyone else ── */}
{view === 'timesheet' &&
(canViewTimesheet && selectedTeamId ? (

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All three fixes look great! One more thing kicked up. This picks the panel off canViewTimesheet alone, which is isAdmin && !isPersonalWorkspace, so it never looks at the Me/Team tab. Checked it on the preview as an admin of a non-personal team, screenshot attached: Me tab is selected, the banner says "Showing your own stats", and it's still rendering AdminTimesheetPanel with a member picker. That panel has no Add Entry and no Break Hours. /app/timesheet redirects into the same view and the Timesheet nav entry is gone from the sidebar, bottom nav and command palette, so an admin can't log a manual past session or see their own break hours unless they switch to their Personal workspace, which does show the personal panel with both. On main an admin got the personal timesheet from the sidebar like anyone else. Would gating on tab === 'me' as well do it?

Image Image

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed. The Me tab now always renders the personal timesheet.

The bug — DashboardPage.tsx:393 chose the panel from canViewTimesheet alone (isAdmin && !isPersonalWorkspace), which has nothing to do with the Me/Team tab. So for any admin, both tabs rendered AdminTimesheetPanel — the team timesheet with the member picker and no Add Entry.

Added showAdminTimesheet = canViewTimesheet && tab === 'team' (:102) and used it to select the panel. Me → PersonalTimesheetPanel (own entries, Add Entry, per-session edit/delete, no member picker); Team → AdminTimesheetPanel for admins.
Scoped the teamApi.getMembers fetch to the same condition (:143-160) so the Me tab no longer fetches the member list it doesn't use.

Dharp02 and others added 2 commits August 7, 2026 11:52
… Team

The Timesheet view picked its panel from canViewTimesheet alone, which only
means "admin on a non-personal workspace" and says nothing about the active
tab. Admins therefore got AdminTimesheetPanel — member picker, no Add Entry —
in both Me and Team.

Gate the admin panel on tab === 'team' so Me → Timesheet is always the signed-in
user's own timesheet, and scope the getMembers fetch to the same condition so
the Me tab stops fetching a member list it doesn't render.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Will address the timezone boundary bug in a separate PR.
@Dharp02

Dharp02 commented Aug 7, 2026

Copy link
Copy Markdown
Collaborator Author

I will fix the deployment pr preview having test roles to test it easily without having to create a new account in a different PR @horner

@Dharp02
Dharp02 merged commit 5a2efc7 into main Aug 7, 2026
14 of 16 checks passed
@github-project-automation github-project-automation Bot moved this from In Progress to Done in Scrum Team Jerry Aug 7, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

4 participants